home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 October: Mac OS SDK / Dev.CD Oct 97 SDK1.toast / Development Kits (Disc 1) / Apple Location Manager / Sources / Utilities.c < prev    next >
Encoding:
Text File  |  1997-05-19  |  21.9 KB  |  850 lines  |  [TEXT/MPS ]

  1. /*
  2.      File:        Utilities.c
  3.  
  4.      Contains:    Location Manager SDK Sample Module handy routines
  5.  
  6.      Version:    ALM SDK 1.0
  7.                  Package:    Location Manager SDK 1.0
  8.  
  9.      Copyright:    © 1984-1997 by Apple Computer, Inc.
  10.                  All rights reserved.
  11.  
  12.      Bugs?:        If you find a problem with this file, use the Apple Bug Reporter
  13.                  stack.  Include the file and version information (from above)
  14.                  in the problem description and send to:
  15.                      Internet:    apple.bugs@apple.com
  16.  
  17. */
  18.  
  19. // -------------------------------------------------------------------------------------
  20.  
  21. // Module Includes...
  22.  
  23. #ifndef        __UTILITIES__
  24. #include    "Utilities.h"
  25. #endif        // __UTILITIES__
  26.  
  27. // Project Includes...
  28.  
  29. #ifndef        __SAMPLE__
  30. #include    "Sample.h"
  31. #endif        // __SAMPLE__
  32.  
  33. // MacOS Includes...
  34.  
  35. #ifndef        __FOLDERS__
  36. #include    <Folders.h>
  37. #endif        // __FOLDERS__
  38.  
  39. #ifndef        __GESTALT__
  40. #include    <Gestalt.h>
  41. #endif        // __GESTALT__
  42.  
  43. #ifndef        __LOWMEM__
  44. #include    <LowMem.h>
  45. #endif        // __LOWMEM__
  46.  
  47. #ifndef        __PLSTRINGFUNCS__
  48. #include    <PLStringFuncs.h>
  49. #endif        // __PLSTRINGFUNCS__
  50.  
  51. #ifndef        __RESOURCES__
  52. #include    <Resources.h>
  53. #endif        // __RESOURCES__
  54.  
  55. #ifndef        __SCRIPT__
  56. #include    <Script.h>
  57. #endif        // __SCRIPT__
  58.  
  59. // ANSI Includes...
  60.  
  61. #include    <string.h>
  62.  
  63. // -------------------------------------------------------------------------------------
  64.  
  65. // Readability Constants...
  66.  
  67. #define        kInvalidRefNum            (-1)
  68. #define        kUseSetting                false
  69. #define        kReadSetting            true
  70. #define        kNoIdleProc                NULL
  71. #define        kNoFilterProc            NULL
  72. #define        kTimeToQuit                (10*60)        // 10 seconds to quit...    
  73. #define        kDontUseIndex            0
  74. #define        kNoFileName                "\p"
  75.  
  76. // Expressive Macros...
  77.  
  78. #define        ShareTime()                { EventRecord e; WaitNextEvent (0, &e, 1, NULL); }
  79. #define        EmptyAEDesc(desc)        { desc.descriptorType = typeNull; \
  80.                                             desc.dataHandle = NULL; }
  81. #define     SetFirstProcess(psn)    { psn.highLongOfPSN = 0; \
  82.                                             psn.lowLongOfPSN = kNoProcess; }
  83.  
  84. // -------------------------------------------------------------------------------------
  85.  
  86. // Local prototypes...
  87.  
  88. static OSErr
  89. QuitApplications (Boolean readOnly);
  90.     // Quit applications if appropriate
  91. static OSErr
  92. SendQuitApplication (Boolean* wasOpen, OSType creator);
  93.     // Send a "quit" AppleEvent to the application whose creator is given...
  94. static Boolean
  95. FindProcessByCreator (ProcessSerialNumber* psn, OSType creator);
  96.     // Given a creator code, return the process serial number of the first matching
  97.     // process...
  98. static Boolean
  99. ProcessManagerAvailable (void);
  100.     // Good way to tell if it's INIT time...
  101. static Boolean
  102. ProcessBecameInvalid (ProcessSerialNumber* psn, UInt32 timeoutInTicks);
  103.     // Little routine to wait for a process to quit (since we can't wait for a reply
  104.     // event)...
  105. static OSErr
  106. MakeAnFSSpec (SInt16 idx, SInt16 vRefNum, SInt32 parID, FSSpec* theSpec);
  107.     // Given a (1-based) index, a volume, and a parent directory ID, go out the
  108.     // resource fork, get the corresponding name, and make an FSSpec for it; do
  109.     // not return fnfErr for non-existant files...
  110. static OSErr
  111. FSpSwapFile (FSSpecPtr theFile, UInt32 fileIdx, SettingPtr theSetting, 
  112.         ALMRebootFlags* flags);
  113.     // Retrieve the file specified by fileIdx (0-based) from theSetting, and, using the
  114.     // FSSpec, adjust the file...
  115. static OSErr
  116. FSpAddFile (FSSpecPtr theFile, UInt32 fileIdx, SettingHandle setting, 
  117.         UInt32* nextByteToUse);
  118.     // Add the forks of the file specified by fileIndex (0-based) to the setting, starting at
  119.     // offset *nextByteToUse, and adjusting *nextByteToUse to the end of the file; also
  120.     // adjust the corresponding info record...
  121. static OSErr
  122. FSpGetFileDates (FSSpecPtr theFile, UInt32* createDate, UInt32* modDate);
  123.     // Given a file, return its creation date and modification date...
  124. static OSErr
  125. FSpSetFileDates (FSSpecPtr theFile, UInt32 createDate, UInt32 modDate);
  126.     // Set the creation and modification dates on the specified file...
  127. static OSErr
  128. NewTempFile (FSSpec* theFile);
  129.     // Create a temporary file, returning its spec...
  130.  
  131. // -------------------------------------------------------------------------------------
  132.  
  133. extern OSErr
  134. UseSetting (GlobalsHandle globals, SettingHandle setting, ALMRebootFlags* flags) {
  135.  
  136.     OSErr        err        = ConfirmFSSpecs (globals);
  137.     SInt8        svState    = HGetState ((Handle) globals);
  138.  
  139.     HLock ((Handle) globals);
  140.  
  141.     if (err == noErr) {
  142.         err = QuitApplications (kUseSetting);
  143.     } // if
  144.  
  145.     if (err == noErr) {
  146.  
  147.         GlobalsPtr    theGlobals    = *globals;
  148.         UInt32        fileCount     = theGlobals->fileCount;
  149.         UInt32        fileIdx;
  150.         SInt8        setState    = HGetState ((Handle) setting);
  151.  
  152.         // Go through and swap each file's data...
  153.         
  154.         if ((**setting).version == kSettingVersion) {
  155.             HLock ((Handle) setting);
  156.             for (fileIdx = 0; fileIdx < fileCount; fileIdx += 1) {
  157.                 err = FSpSwapFile (&theGlobals->theFiles[fileIdx], fileIdx, *setting, flags);
  158.                 if (err != noErr) {
  159.                     break;
  160.                 } // if
  161.             } // for
  162.         } else {
  163.             err = paramErr;
  164.         } // if
  165.         
  166.         HSetState ((Handle) setting, setState);
  167.  
  168.     } // if
  169.  
  170.     HSetState ((Handle) globals, svState);
  171.  
  172.     return err;
  173.  
  174. } // UseSetting
  175.  
  176. // -------------------------------------------------------------------------------------
  177.  
  178. extern OSErr
  179. ReadSetting (GlobalsHandle globals, SettingHandle setting) {
  180.  
  181.     OSErr        err        = ConfirmFSSpecs (globals);
  182.     SInt8        svState    = HGetState ((Handle) globals);
  183.  
  184.     HLock ((Handle) globals);
  185.  
  186.     if (err == noErr) {
  187.         err = QuitApplications (kReadSetting);
  188.     } // if
  189.  
  190.     if (err == noErr) {
  191.         
  192.         GlobalsPtr    theGlobals    = *globals;
  193.         UInt32        fileCount     = theGlobals->fileCount;
  194.         UInt32        fileIdx;
  195.         UInt32        freeDataOffset;
  196.         
  197.         // First make room for the fixed-size records; we'll add space for each
  198.         // file's data later...
  199.         
  200.         if (fileCount > 0) {
  201.         
  202.             freeDataOffset = sizeof (SettingRec) + 
  203.                                 sizeof (FileInfoRec) * (fileCount - kVariableLengthArray);
  204.             SetHandleSize ((Handle) setting, freeDataOffset);
  205.             err = MemError ();
  206.             
  207.             // Go through and add each file's data...
  208.             
  209.             if (err == noErr) {
  210.                 (**setting).fileCount = 0;
  211.                 (**setting).version = kSettingVersion;
  212.                 for (fileIdx = 0; fileIdx < fileCount; fileIdx += 1) {
  213.                     err = FSpAddFile (&theGlobals->theFiles[fileIdx], fileIdx, setting, &freeDataOffset);
  214.                     if (err != noErr) {
  215.                         break;
  216.                     } // if
  217.                     (**setting).fileCount = fileIdx + 1;
  218.                 } // for
  219.             } // if
  220.  
  221.         } // if
  222.         
  223.     } // if
  224.  
  225.     HSetState ((Handle) globals, svState);
  226.  
  227.     return err;
  228.  
  229. } // ReadSetting
  230.  
  231. // -------------------------------------------------------------------------------------
  232.  
  233. extern OSErr
  234. ConfirmFSSpecs (GlobalsHandle globals) {
  235.  
  236.     OSErr        err        = noErr;
  237.     SInt8        svState    = HGetState ((Handle) globals);
  238.     
  239.     // Our assumption is that if the fileCount is 0, we need to go out and
  240.     // create FSSpecs for the files...
  241.     
  242.     if ((**globals).fileCount == 0) {
  243.     
  244.         SInt16        fileCount;
  245.         
  246.         HUnlock ((Handle) globals);    // May need to resize...
  247.  
  248.         // Each file occupies two resources; one is a string, and the other is
  249.         // a bunch of flags (it's done this way to avoid some messy variable-size
  250.         // data), but we only need the string at this point...
  251.  
  252.         fileCount = Count1Resources (kFilesRsrcType);
  253.  
  254.         if (fileCount > 0) {
  255.         
  256.             SInt16        vRefNum;
  257.             SInt32        parID;
  258.             GlobalsPtr    theGlobals;
  259.         
  260.             // Prepare the globals to receive our data, and look up the preferences
  261.             // folder just this once...
  262.         
  263.             SetHandleSize ((Handle) globals, 
  264.                     sizeof (Globals) + sizeof (FSSpec) * (fileCount - kVariableLengthArray));
  265.             err = MemError ();
  266.             if (err == noErr) {
  267.                 (**globals).fileCount = fileCount;
  268.                 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, 
  269.                             kDontCreateFolder, &vRefNum, &parID);
  270.             } // if
  271.  
  272.             // With all that, we can lock down the globals, and stuff FSSpecs into
  273.             // the array one at a time...
  274.  
  275.             if (err == noErr) {
  276.             
  277.                 SInt16        curFileIdx;
  278.             
  279.                 HLock ((Handle) globals);
  280.                 
  281.                 theGlobals = *globals;
  282.  
  283.                 for (curFileIdx = 0; curFileIdx < fileCount; curFileIdx += 1) {
  284.                 
  285.                     err = MakeAnFSSpec (curFileIdx + 1, vRefNum, parID, 
  286.                             &theGlobals->theFiles[curFileIdx]);
  287.                     if (err != noErr) {
  288.                         break;
  289.                     } // if
  290.                 
  291.                 } // for
  292.             
  293.             } // if
  294.  
  295.         } // if
  296.     
  297.     } // if
  298.     
  299.     HSetState ((Handle) globals, svState);
  300.  
  301.     return err;
  302.  
  303. } // ConfirmFSSpecs
  304.  
  305. // -------------------------------------------------------------------------------------
  306.  
  307. extern void
  308. InsParamStr (StringPtr matchThis, StringPtr replaceWithThis, StringPtr replaceInThis) {
  309.  
  310.     UInt8        matchLen        = StrLength (matchThis);
  311.     UInt8        replaceLen        = StrLength (replaceWithThis);
  312.     UInt8        replaceInLen    = StrLength (replaceInThis);
  313.     Ptr            matchPtr;
  314.     Str255        tempStr;
  315.  
  316.     matchPtr = PLstrstr (replaceInThis, matchThis);
  317.     PLstrcpy (tempStr, replaceInThis);
  318.  
  319.     if (matchPtr != NULL) {
  320.     
  321.         UInt8        matchPos    = (UInt32) matchPtr - (UInt32) replaceInThis;
  322.         UInt8        contPos        = matchPos + matchLen;
  323.         UInt8        frontLen    = matchPos - 1;
  324.         UInt8        backLen        = replaceInLen - frontLen - matchLen;
  325.         
  326.         BlockMoveData (&replaceWithThis[1], &replaceInThis[matchPos], replaceLen);
  327.         BlockMoveData (&tempStr[contPos], &replaceInThis[frontLen + replaceLen + 1], backLen);
  328.         replaceInThis[0] = ((UInt32) replaceInLen - (UInt32) matchLen + (UInt32) replaceLen);
  329.         
  330.     } // if
  331.  
  332. } // InsParamStr
  333.  
  334. // -------------------------------------------------------------------------------------
  335.  
  336. static OSErr
  337. QuitApplications (Boolean readOnly) {
  338.  
  339.     OSErr                        err        = noErr;
  340.     ApplicationListHandle        appsHandle;
  341.  
  342.     appsHandle = (ApplicationListHandle) Get1Resource (kAppsRsrcType, kAppsRsrcID);
  343.  
  344.     if (appsHandle != NULL) {
  345.         
  346.         UInt32                    curApp;
  347.         ApplicationListPtr        apps;
  348.         UInt8                    flags;
  349.         Boolean                    wasOpen;
  350.     
  351.         HLock ((Handle) appsHandle);
  352.         apps = *appsHandle;
  353.         
  354.         for (curApp = 0; curApp < apps->applicationCount; curApp += 1) {
  355.             flags = apps->applications[curApp].flags;
  356.             if ((flags == kAppListQuitOnGetOrSet) || 
  357.                     (readOnly && (flags == kAppListQuitOnGet)) || 
  358.                         (!readOnly && (flags == kAppListQuitOnSet))) {
  359.                 err = SendQuitApplication (&wasOpen, apps->applications[curApp].applicationCode);
  360.                 if (err != noErr) {
  361.                     break;
  362.                 } // if
  363.             } // if
  364.         } // for
  365.     
  366.         HUnlock ((Handle) appsHandle);
  367.         ReleaseResource ((Handle) appsHandle);
  368.         
  369.     } else {
  370.     
  371.         err = resNotFound;
  372.     
  373.     } // if
  374.  
  375.     return err;
  376.  
  377. } // QuitApplications
  378.  
  379. // ------------------------------------------------------------------------------------
  380.  
  381. static OSErr
  382. SendQuitApplication (Boolean* wasOpen, OSType creator) {
  383.  
  384.     OSErr                    err                    = noErr;
  385.     ProcessSerialNumber        applicationPSN;
  386.     AEAddressDesc            applicationAddr;
  387.     AppleEvent                quitEvent;
  388.     AppleEvent                theReply;
  389.     
  390.     EmptyAEDesc (applicationAddr);
  391.     EmptyAEDesc (quitEvent);
  392.     EmptyAEDesc (theReply);
  393.  
  394.     // Find a process matching the creator, if any; if it is found, send it an
  395.     // AppleEvent to quit (it does support the core suite, right?)...
  396.     
  397.     *wasOpen = FindProcessByCreator (&applicationPSN, creator);
  398.     
  399.     if (*wasOpen) {
  400.         if (err == noErr) {
  401.             err = AECreateDesc (typeProcessSerialNumber, &applicationPSN, 
  402.                         sizeof (applicationPSN), &applicationAddr);
  403.         } // if
  404.         if (err == noErr) {
  405.             err = AECreateAppleEvent (kCoreEventClass, kAEQuitApplication,
  406.                         &applicationAddr, kAutoGenerateReturnID, kAnyTransactionID, 
  407.                         &quitEvent); 
  408.         } // if
  409.         if (err == noErr) {
  410.             err = AESend (&quitEvent, &theReply, kAENoReply | kAENeverInteract,
  411.                         kAEHighPriority, kAEDefaultTimeout, kNoIdleProc,
  412.                         kNoFilterProc);
  413.         } // if
  414.         if (err == noErr) {
  415.             if (!ProcessBecameInvalid (&applicationPSN, kTimeToQuit)) {
  416.                 err = kSampleCouldNotQuitIdx;    // We'll tell the user we tried...
  417.             } // if
  418.         } // if
  419.     } // if
  420.  
  421.     (void) AEDisposeDesc (&applicationAddr);
  422.     (void) AEDisposeDesc (&quitEvent);
  423.  
  424.     return err;
  425.  
  426. } // SendQuitApplication
  427.  
  428. // ------------------------------------------------------------------------------------
  429.  
  430. static Boolean
  431. FindProcessByCreator (ProcessSerialNumber* psn, OSType creator) {
  432.  
  433.     OSErr                    err                 = noErr;
  434.     Boolean                    found                = false;
  435.     ProcessInfoRec            curProcessInfo;
  436.     ProcessSerialNumber        curPSN; 
  437.     
  438.     SetFirstProcess (curPSN);
  439.     
  440.     // If we're running at INIT time, none of this will work (and we don't need to
  441.     // do it anyway!), so 
  442.     
  443.     if (ProcessManagerAvailable ()) {
  444.  
  445.         do {
  446.             curProcessInfo.processInfoLength     = sizeof (curProcessInfo);
  447.             curProcessInfo.processName            = NULL;
  448.             curProcessInfo.processAppSpec        = NULL;
  449.             if (err == noErr) {
  450.                 err = GetNextProcess (&curPSN);
  451.             } // if
  452.             if (err == noErr) {
  453.                 err = GetProcessInformation (&curPSN, &curProcessInfo);
  454.             } // if
  455.             if (err == noErr) {
  456.                 if (curProcessInfo.processSignature == creator) {
  457.                     *psn = curPSN;
  458.                     found = true;
  459.                     break;
  460.                 } // if
  461.             } // if
  462.         } while (err == noErr);
  463.     
  464.     } // if
  465.  
  466.     return found;
  467.  
  468. } // FindProcessByCreator
  469.  
  470. // ------------------------------------------------------------------------------------
  471.  
  472. static Boolean
  473. ProcessManagerAvailable (void) {
  474.  
  475.     OSErr        err;
  476.     SInt32        response;
  477.     Boolean        reply;
  478.     
  479.     err = Gestalt (gestaltOSAttr, &response);
  480.     
  481.     reply = (err == noErr) && (response & (1 << gestaltLaunchControl));
  482.     
  483.     return reply;
  484.  
  485. } // ProcessManagerAvailable
  486.  
  487. // ------------------------------------------------------------------------------------
  488.  
  489. static Boolean
  490. ProcessBecameInvalid (ProcessSerialNumber* psn, UInt32 timeoutInTicks) {
  491.  
  492.     OSErr            err                 = noErr;
  493.     Boolean            stillAround         = true;
  494.     UInt32            startTime             = LMGetTicks ();
  495.     UInt32            curTime                = startTime;
  496.     UInt32            endTime                = startTime + timeoutInTicks;
  497.     ProcessInfoRec    processInfo;
  498.     
  499.     // Wait around, being background friendly, for the specified process to 
  500.     // terminate...
  501.  
  502.     do {
  503.         ShareTime ();
  504.         if (err == noErr) {
  505.             processInfo.processInfoLength     = sizeof (processInfo);
  506.             processInfo.processName            = NULL;
  507.             processInfo.processAppSpec        = NULL;
  508.             err = GetProcessInformation (psn, &processInfo);
  509.             if ((err == paramErr) || (err == procNotFound)) {
  510.                 stillAround = false;
  511.                 break;
  512.             } // if
  513.         } // if
  514.         curTime = LMGetTicks ();
  515.     } while (curTime < endTime);
  516.     
  517.     return !stillAround;
  518.  
  519. } // ProcessBecameInvalid
  520.  
  521. // ------------------------------------------------------------------------------------
  522.  
  523. static OSErr
  524. MakeAnFSSpec (SInt16 idx, SInt16 vRefNum, SInt32 parID, FSSpec* theSpec) {
  525.  
  526.     OSErr                    err                    = noErr;
  527.     FileEntryHandle            fileName;
  528.  
  529.     fileName = (FileEntryHandle) Get1IndResource (kFilesRsrcType, idx);
  530.     
  531.     if (fileName != NULL) {
  532.         HLock ((Handle) fileName);
  533.         err = FSMakeFSSpec (vRefNum, parID, (StringPtr) *fileName, theSpec);
  534.         if (err == fnfErr) {
  535.             err = noErr;
  536.         } // if
  537.         HUnlock ((Handle) fileName);
  538.         ReleaseResource ((Handle) fileName);
  539.     } else {
  540.         err = resNotFound;
  541.     } // if
  542.  
  543.     return err;
  544.  
  545. } // MakeAnFSSpec
  546.  
  547. // ------------------------------------------------------------------------------------
  548.  
  549. static OSErr
  550. FSpSwapFile (FSSpecPtr theFile, UInt32 fileIdx, SettingPtr theSetting, 
  551.         ALMRebootFlags* flags) {
  552.  
  553.     OSErr                    err                    = noErr;
  554.     FSSpec                    tempFile;
  555.     SInt16                    dataForkRef            = kInvalidRefNum;
  556.     SInt16                    resForkRef            = kInvalidRefNum;
  557.     Ptr                        dataForkStart;
  558.     Ptr                        resForkStart;
  559.     SInt32                    dataForkLen;
  560.     SInt32                    resForkLen;
  561.     FSSpec                    destSpec;
  562.  
  563.     // Our strategy here is to create a temp file, put our data in it, close the file,
  564.     // swap it with the real one (if the real one exists), then reset the creation and
  565.     // modification dates....
  566.     
  567.     // Create a temp file and open its forks...
  568.     
  569.     if (err == noErr) {
  570.         err = NewTempFile (&tempFile);
  571.     } // if
  572.     if (err == noErr) {
  573.         err = FSpOpenDF (&tempFile, fsRdWrPerm, &dataForkRef);
  574.     } // if
  575.     if (err == noErr) {
  576.         err = FSpOpenRF (&tempFile, fsRdWrPerm, &resForkRef);
  577.     } // if
  578.  
  579.     // Write the data out of the setting...
  580.     
  581.     dataForkStart    = ((Ptr) theSetting) + theSetting->fileInfo[fileIdx].dataForkOffset;
  582.     resForkStart    = ((Ptr) theSetting) + theSetting->fileInfo[fileIdx].resForkOffset;
  583.     dataForkLen        = theSetting->fileInfo[fileIdx].dataForkLen;
  584.     resForkLen        = theSetting->fileInfo[fileIdx].resForkLen;
  585.     
  586.     if (err == noErr) {
  587.         err = FSWrite (dataForkRef, &dataForkLen, dataForkStart);
  588.     } // if
  589.     if (err == noErr) {
  590.         err = FSWrite (resForkRef, &resForkLen, resForkStart);
  591.     } // if
  592.  
  593.     // Swap the files (or move the one, if the other does not exist)...
  594.         
  595.     if (err == noErr) {
  596.         err = FSpExchangeFiles (theFile, &tempFile);
  597.         if (err == noErr) {
  598.             err = FSpDelete (&tempFile);
  599.         } else if (err == fnfErr) {
  600.             err = FSMakeFSSpec (theFile->vRefNum, theFile->parID, kNoFileName, &destSpec);
  601.             if (err == noErr) {
  602.                 err = FSpCatMove (&tempFile, &destSpec);
  603.             } // if
  604.         } // if
  605.     } // if
  606.  
  607.     // Restore the file's dates...
  608.     
  609.     if (err == noErr) {
  610.         err = FSpSetFileDates (theFile, theSetting->fileInfo[fileIdx].dateCreated,
  611.                      theSetting->fileInfo[fileIdx].dateModified);
  612.     } // if
  613.     
  614.     // Now, the fiddly part--using the index, get the reboot flags for this file...
  615.     
  616.     if (err == noErr) {
  617.     
  618.         // We'll do it this "roundabout" way so that we know we're correctly matching
  619.         // resource ids...
  620.  
  621.         FileEntryHandle        fileName     = (FileEntryHandle) Get1IndResource (kFilesRsrcType, fileIdx + 1);
  622.         SInt16                rsrcID;
  623.         ResType                rsrcType;
  624.         Str255                rsrcName;
  625.         FileFlagsHandle        theFlags;
  626.         
  627.         if (fileName != NULL) {
  628.  
  629.             GetResInfo ((Handle) fileName, &rsrcID, &rsrcType, rsrcName);
  630.             err = ResError ();
  631.             if (err == noErr) {
  632.                 theFlags = (FileFlagsHandle) Get1Resource (kRestartRsrcType, rsrcID);
  633.                 if ((**theFlags).flags & kRestartIfChanged != 0) {
  634.                     *flags = kALMExtensions;
  635.                 } // if
  636.                 ReleaseResource ((Handle) theFlags);
  637.             } // if
  638.             ReleaseResource ((Handle) fileName);
  639.  
  640.         } else {
  641.             err = resNotFound;
  642.         } // if
  643.     
  644.     } // if
  645.  
  646.     // Cleanup...
  647.  
  648.     if (dataForkRef != kInvalidRefNum) {
  649.         (void) FSClose (dataForkRef);
  650.     } // if
  651.     if (resForkRef != kInvalidRefNum) {
  652.         (void) FSClose (resForkRef);
  653.     } // if
  654.  
  655.     return err;
  656.  
  657. } // FSpSwapFile
  658.  
  659. // ------------------------------------------------------------------------------------
  660.  
  661. static OSErr
  662. FSpAddFile (FSSpecPtr theFile, UInt32 fileIdx, SettingHandle setting, 
  663.         UInt32* nextByteToUse) {
  664.  
  665.     OSErr                    err                    = noErr;
  666.     SInt16                    dataForkRef            = kInvalidRefNum;
  667.     SInt16                    resForkRef            = kInvalidRefNum;
  668.     SInt32                    dataForkLen;
  669.     SInt32                    resForkLen;
  670.     SInt8                    svState                = HGetState ((Handle) setting);
  671.     SInt32                    newHandleSize;
  672.     UInt32                    freeDataOffset        = *nextByteToUse;
  673.     UInt32                    createDate;
  674.     UInt32                    modDate;
  675.     
  676.     // Open both forks (no dining philosophers, please), and get the Dates...
  677.     
  678.     if (err == noErr) {
  679.         err = FSpGetFileDates (theFile, &createDate, &modDate);
  680.     } // if
  681.     if (err == noErr) {
  682.         err = FSpOpenDF (theFile, fsRdPerm, &dataForkRef);
  683.     } // if
  684.     if (err == noErr) {
  685.         err = FSpOpenRF (theFile, fsRdPerm, &resForkRef);
  686.     } // if
  687.  
  688.     // Determine how long the forks are...
  689.     
  690.     if (err == noErr) {
  691.         err = GetEOF (dataForkRef, &dataForkLen);
  692.     } // if
  693.     if (err == noErr) {
  694.         err = GetEOF (resForkRef, &resForkLen);
  695.     } // if
  696.  
  697.     // Resize the handle so we can store the data in it, then lock it down for reading...
  698.     
  699.     if (err == noErr) {
  700.         newHandleSize = freeDataOffset + dataForkLen + resForkLen;
  701.         SetHandleSize ((Handle) setting, newHandleSize);
  702.         err = MemError ();
  703.     } // if
  704.     if (err == noErr) {
  705.     
  706.         SettingPtr        theSetting;
  707.         FileInfoPtr        theFileInfo;
  708.         Ptr                startAddr;
  709.     
  710.         HLock ((Handle) setting);
  711.         
  712.         theSetting = *setting;
  713.         theFileInfo = &theSetting->fileInfo[fileIdx];
  714.         startAddr = ((Ptr) theSetting) + freeDataOffset;
  715.         
  716.         // Record information about the file 
  717.         
  718.         theFileInfo->dateCreated        = createDate;
  719.         theFileInfo->dateModified        = modDate;
  720.         theFileInfo->dataForkOffset        = freeDataOffset;
  721.         theFileInfo->dataForkLen        = dataForkLen;
  722.         theFileInfo->resForkOffset        = freeDataOffset + dataForkLen;
  723.         theFileInfo->resForkLen            = resForkLen;
  724.         
  725.         // Finally, read in the forks at their alloted locations...
  726.         
  727.         if ((err == noErr) && (dataForkLen > 0)) {
  728.             err = FSRead (dataForkRef, &dataForkLen, startAddr);
  729.         } // if
  730.         if ((err == noErr) && (resForkLen > 0)) {
  731.             err = FSRead (resForkRef, &resForkLen, startAddr + dataForkLen);
  732.         } // if
  733.         
  734.     } // if
  735.  
  736.     // Cleanup...
  737.  
  738.     if (dataForkRef != kInvalidRefNum) {
  739.         (void) FSClose (dataForkRef);
  740.     } // if
  741.     if (resForkRef != kInvalidRefNum) {
  742.         (void) FSClose (resForkRef);
  743.     } // if
  744.  
  745.     *nextByteToUse = newHandleSize;
  746.  
  747.     HSetState ((Handle) setting, svState);
  748.  
  749.     return err;
  750.  
  751. } // FSpAddFile
  752.  
  753. // ------------------------------------------------------------------------------------
  754.  
  755. static OSErr
  756. FSpGetFileDates (FSSpecPtr theFile, UInt32* createDate, UInt32* modDate) {
  757.  
  758.     OSErr            err        = noErr;
  759.  
  760.     if (err == noErr) {
  761.     
  762.         HParamBlockRec        paramBlock;
  763.         HFileParam*            fileParam = ¶mBlock.fileParam;
  764.         
  765.         fileParam->ioCompletion        = NULL;
  766.         fileParam->ioNamePtr        = (StringPtr) theFile->name;
  767.         fileParam->ioVRefNum        = theFile->vRefNum;
  768.         fileParam->ioFDirIndex        = kDontUseIndex;
  769.         fileParam->ioDirID            = theFile->parID;
  770.         
  771.         err = PBHGetFInfoSync (¶mBlock);
  772.         
  773.         if (err == fnfErr) {
  774.             *createDate        = 0;
  775.             *modDate        = 0;
  776.             err = noErr;
  777.         } else {
  778.             *createDate        = fileParam->ioFlCrDat;
  779.             *modDate        = fileParam->ioFlMdDat;
  780.         } // if
  781.         
  782.     } // if
  783.  
  784.     return err;
  785.  
  786. } // FSpGetFileDates
  787.  
  788. // ------------------------------------------------------------------------------------
  789.  
  790. static OSErr
  791. FSpSetFileDates (FSSpecPtr theFile, UInt32 createDate, UInt32 modDate) {
  792.  
  793.     OSErr            err        = noErr;
  794.  
  795.     if (err == noErr) {
  796.     
  797.         HParamBlockRec        paramBlock;
  798.         HFileParam*            fileParam = ¶mBlock.fileParam;
  799.         
  800.         fileParam->ioCompletion        = NULL;
  801.         fileParam->ioNamePtr        = (StringPtr) theFile->name;
  802.         fileParam->ioVRefNum        = theFile->vRefNum;
  803.         fileParam->ioFDirIndex        = kDontUseIndex;
  804.         fileParam->ioDirID            = theFile->parID;
  805.         
  806.         err = PBHGetFInfoSync (¶mBlock);
  807.         
  808.         if (err == noErr) {
  809.             // Need to restore PBHGetFInfoSync because it changes ioDirID to a fileID
  810.             // but we want the dirID for the PBHSetFInfoSync call to work!
  811.             fileParam->ioDirID            = theFile->parID;
  812.             fileParam->ioFlCrDat        = createDate;
  813.             fileParam->ioFlMdDat        = modDate;
  814.             err = PBHSetFInfoSync (¶mBlock);
  815.         } // if
  816.         
  817.     } // if
  818.  
  819.     return err;
  820.  
  821. } // FSpSetFileDates
  822.  
  823. // ------------------------------------------------------------------------------------
  824.  
  825. static OSErr
  826. NewTempFile (FSSpec* theFile) {
  827.  
  828.     OSErr            err        = noErr;
  829.     SInt16            vRefNum;
  830.     SInt32            parID;
  831.  
  832.     if (err == noErr) {
  833.     
  834.         err = FindFolder (kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
  835.                 &vRefNum, &parID);
  836.         if (err == noErr) {
  837.             err = FSMakeFSSpec (vRefNum, parID, "\pModule Temp", theFile);
  838.             if (err == noErr) {
  839.                 err = dupFNErr;
  840.             } else if (err == fnfErr) {
  841.                 err = FSpCreate (theFile, '\?\?\?\?', '\?\?\?\?', smSystemScript);
  842.             } // if
  843.         } // if
  844.         
  845.     } // if
  846.  
  847.     return err;
  848.  
  849. } // NewTempFile
  850.